﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using Noris.Schedule.Support.Core;
using Noris.Schedule.Support;

namespace Noris.Schedule.UI.Forms
{
	public partial class ProcessForm : Form
	{
		public ProcessForm()
		{
			InitializeComponent();
			this.SizeChanged += new EventHandler(_ProcessForm_SizeChanged);
            this.FormClosing += new FormClosingEventHandler(_ProcessForm_FormClosing);
			this._ProcessInnerProgressStyle = this._InnerProgress.Style;
			this._LastWindowState = FormWindowState.Normal;
		}
		private FormWindowState _LastWindowState;
		#region SEPARATE THREAD START
		/// <summary>
		/// Vytvoří a vrátí formulář, který je vytvořen v jiném vláknu (thread), než je volající.
		/// Důvod: volající thread může být ve svém postupu pozastaven, když zadá synchronní požadavek do fronty práce (která jede ve svém soukromém threadu).
		/// Když pak výkonná metoda z threadu fronty práce zavolá technikou callback aktualizaci tohoto okna, a toto okno by bylo v threadu volající metody,
		///   pak by tento form byl pozastaven a neaktivní, nereaguje na volání. A požadavky na synchronní Invoke pak zastaví celý proces,
		///   protože dojde k deadlocku (volající thread = thread formu, čeká na dokončení úkolu z fronty práce, tam dojde k synchronní invokaci formu, který je v threadu, který spí...)
		/// </summary>
		/// <returns></returns>
		public static ProcessForm CreateInSeparateThread()
		{
			// Owner threadem okna není ten thread, který objekt Form vytvořil (create),
			//  ale thread, který jej rozsvítí (Show)  !!!  
			// Proto mohu v aktuálním threadu okno vytvořit, ale zobrazit jej musím ze separátního threadu.
			ProcessForm pForm = new ProcessForm();
			pForm._ArEvent.Reset();

			pForm._OwnerThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(_ShowInSeparateThread));
			pForm._OwnerThread.Name = "ProcessFormThread";
			pForm._OwnerThread.SetApartmentState(ApartmentState.STA);
			pForm._OwnerThread.Start(pForm);

			pForm._ArEvent.WaitOne();        // Počkám, až metoda _ShowInSeparateThread() rozsvítí Form, a předá mi signál přes _ArEvent.Set()

			return pForm;
		}
		private static void _ShowInSeparateThread(object data)
		{
			if (!(data is ProcessForm)) return;
			try
			{
				ProcessForm pForm = data as ProcessForm;
				pForm.TopLevel = true;
				pForm.StartPosition = FormStartPosition.Manual;
				pForm.StartPosition = FormStartPosition.CenterScreen;

				// Zdejší vlákno se zde zasekne na dobu života Formu. V háčku OnShown se předá signál do volajícího threadu, že ten může pokračovat.
				pForm.ShowDialog();
			}
			catch (Exception exc)
			{
				Dialog.ShowError(exc, MessageInfo.Get("Došlo k chybě v okně ProcessForm:"));
			}
		}
		protected override void OnShown(EventArgs e)
		{
			base.OnShown(e);
			this._ArEvent.Set();            // Teď se uvolní metoda CreateInSeparateThread() a vrátí rozsvícený Form.
		}
		private Thread _OwnerThread;
		private bool _CloseThread;
		private AutoResetEvent _ArEvent = new AutoResetEvent(false);
		#endregion
		#region PROPERTY
		/// <summary>
		/// Titulek v groupboxu
		/// </summary>
		[Browsable(true)]
		[Category("Appearance")]
		[Description("Titulek v groupboxu")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public string ProcessTitle
		{
			get { return this._GetText(this._ProcesGrp); }
			set { this._SetText(this._ProcesGrp, value);}
		}
		/// <summary>
		/// Text nad horním progressbarem
		/// </summary>
		[Browsable(true)]
		[Category("Appearance")]
		[Description("Text nad horním progressbarem")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public string ProcessMainText
		{
			get { return this._GetText(this._MainLbl); }
			set { this._SetText(this._MainLbl, value); }
		}
		/// <summary>
		/// Text nad dolním progressbarem
		/// </summary>
		[Browsable(true)]
		[Category("Appearance")]
		[Description("Text nad dolním progressbarem")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public string ProcessInnerText
		{
			get { return this._GetText(this._InnerLbl); }
			set { this._SetText(this._InnerLbl, value); }
		}
		/// <summary>
		/// Hodnota (0-1) v horním progressbaru
		/// </summary>
		[Browsable(true)]
		[Category("Appearance")]
		[Description("Hodnota (0-1) v horním progressbaru")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public double ProcessMainProgress
		{
			get { return this._GetValue(this._MainProgress); }
			set { this._SetValue(this._MainProgress, value); }
		}
		/// <summary>
		/// Styl použitý pro zobrazení inner progressbaru
		/// </summary>
		[Browsable(true)]
		[Category("Appearance")]
		[Description("Styl použitý pro zobrazení inner progressbaru")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public ProgressBarStyle ProcessInnerProgressStyle
		{
			get { return this._ProcessInnerProgressStyle; }
			set
			{
				if (value != this._ProcessInnerProgressStyle)
				{
					this._ProcessInnerProgressStyle = value;
					this._SetStyle(this._InnerProgress, value);
				}
			}
		}
		/// <summary>
		/// privátní kopie hodnoty this._InnerProgress.Style, abychom pro čtení nemuseli invokovat read, a při setování invokovali set jen při změně.
		/// </summary>
		private ProgressBarStyle _ProcessInnerProgressStyle;
		/// <summary>
		/// Hodnota (0-1) v dolním progressbaru
		/// </summary>
		[Browsable(true)]
		[Category("Appearance")]
		[Description("Hodnota (0-1) v dolním progressbaru")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public double ProcessInnerProgress
		{
			get { return this._GetValue(this._InnerProgress); }
			set { this._SetValue(this._InnerProgress, value); }
		}
		/// <summary>
		/// Metoda vyvolá Refresh vizuálních prvků formu.
		/// </summary>
		public void ProcessRefresh()
		{
			this._ProcessRefresh();
		}
		/// <summary>
		/// Metoda zajistí zavření okna (Hide a Close), včetně invokace do patřičného threadu.
		/// </summary>
		public void ProcessClose()
		{
			if (this.InvokeRequired)
			{
				this._CloseThread = true;          // Zajistí ukončení threadu.
				this.BeginInvoke(new Action(ProcessClose));
			}
			else
			{
				this._CloseThread = true;          // Zajistí ukončení threadu.
				this.Hide();
				this.Close();
				this.Dispose();
			}
		}
		#endregion
		#region Events
		void _ProcessForm_SizeChanged(object sender, EventArgs e)
		{
			if (this.WindowState == FormWindowState.Minimized && this._LastWindowState != FormWindowState.Minimized)
			{
				if (Steward.MainForm != null)
				{
					if (Steward.MainForm.InvokeRequired)
						Steward.MainForm.BeginInvoke(new Action(delegate() { Steward.MainForm.WindowState = FormWindowState.Minimized; }));
					else
						Steward.MainForm.WindowState = FormWindowState.Minimized;
				}
				this._LastWindowState = FormWindowState.Minimized;
				this.WindowState = FormWindowState.Normal;
			}
		}
        void _ProcessForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.ApplicationExitCall)
                this.ProcessClose();
        }
        #endregion
		#region THREAD INVOKE
		/// <summary>Delegát pro načtení textové hodnoty z daného controlu</summary>
		private delegate string _GetStringDlg(Control control);
		/// <summary>Delegát pro vložení textové hodnoty do daného controlu</summary>
		private delegate void _SetStringDlg(Control control, string text);
		/// <summary>Delegát pro načtení double hodnoty z daného controlu</summary>
		private delegate double _GetDoubleDlg(ProgressBar control);
		/// <summary>Delegát pro vložení double hodnoty do daného controlu</summary>
		private delegate void _SetDoubleDlg(ProgressBar control, double value);
		/// <summary>Delegát pro vložení ProgressBarStyle hodnoty do daného controlu</summary>
		private delegate void _SetStyleDlg(ProgressBar control, ProgressBarStyle value);
		/// <summary>Delegát pro vyvolání refreshe</summary>
		private delegate void _CallRefresh();
		/// <summary>
		/// Vrátí string z property Text předaného Controlu. Invokaci si zajistí sám.
		/// </summary>
		/// <param name="control"></param>
		/// <returns></returns>
		private string _GetText(Control control)
		{
			if (control.InvokeRequired)
			{
				return (string)(control.Invoke(new _GetStringDlg(this._GetText), control));
			}
			else
				return control.Text;
		}
		/// <summary>
		/// Nastaví do daného controlu hodnotu do property Text z předaného parametru. Invokaci si zajistí sám.
		/// </summary>
		/// <param name="control"></param>
		/// <param name="text"></param>
		private void _SetText(Control control, string text)
		{
			if (control.InvokeRequired)
			{
				control.BeginInvoke(new _SetStringDlg(this._SetText), control, text);
			}
			else
				control.Text = text;
		}
		/// <summary>
		/// Vrátí double z property Value předaného Controlu. Invokaci si zajistí sám.
		/// </summary>
		/// <param name="control"></param>
		/// <returns></returns>
		private double _GetValue(ProgressBar control)
		{
			if (control.InvokeRequired)
			{
				return (double)(control.Invoke(new _GetDoubleDlg(this._GetValue), control));
			}
			else
			{
				double value = 0D;
				double dimen = (double)(control.Maximum - control.Minimum);
				if (dimen > 0D)
					value = ((double)(control.Value - control.Minimum)) / dimen;
				return value;
			}
		}
		/// <summary>
		/// Nastaví do daného controlu hodnotu do property Text z předaného parametru. Invokaci si zajistí sám.
		/// </summary>
		/// <param name="control"></param>
		/// <param name="text"></param>
		private void _SetValue(ProgressBar control, double value)
		{
			int v = (int)value;
			if (v < 0) value = 0D;            // Pokud by sem vlezla hodnota value = NaN (tj. 0 / 0), tak dáme value = 0D!
			
			if (control.InvokeRequired)
			{
				control.BeginInvoke(new _SetDoubleDlg(this._SetValue), control, value);
			}
			else
			{
				double dimen = (double)(control.Maximum - control.Minimum);
				if (dimen > 0D)
				{
					double r = (value < 0D ? 0D : (value > 1D ? 1D : value));         // Do intervalu { 0 ÷ 1 }
					double s = Math.Round((double)control.Minimum + r * dimen, 0);    // Nová hodnota, zaokrouhlená, double
					int progressValue = (int)s;
					if (progressValue < control.Minimum)
						progressValue = control.Minimum;
					if (progressValue > control.Maximum)
						progressValue = control.Maximum;

					control.Value = progressValue;
				}
			}
		}
		/// <summary>
		/// Nastaví do daného controlu hodnotu do property Style z předaného parametru. Invokaci si zajistí sám.
		/// </summary>
		/// <param name="control"></param>
		/// <param name="value"></param>
		private void _SetStyle(ProgressBar control, ProgressBarStyle value)
		{
			if (control.InvokeRequired)
			{
				control.BeginInvoke(new _SetStyleDlg(this._SetStyle), control, value);
			}
			else
			{
				control.Style = value;
			}
		}
		/// <summary>
		/// Metoda zavolá Refresh na prvky
		/// </summary>
		/// <param name="control"></param>
		private void _ProcessRefresh()
		{
			if (this.InvokeRequired)
			{
				this.BeginInvoke(new _CallRefresh(this._ProcessRefresh));
			}
			else
			{
				this._MainLbl.Refresh();
				this._MainProgress.Refresh();
				this._InnerLbl.Refresh();
				this._InnerProgress.Refresh();
				System.Threading.Thread.Sleep(10);
			}
		}
		#endregion
	}
}
